package com.dwarfeng.familyhelper.webapi.node.controller.v1.life;

import com.dwarfeng.dutil.basic.io.IOUtil;
import com.dwarfeng.familyhelper.life.sdk.bean.entity.JSFixedFastJsonPbFileInfo;
import com.dwarfeng.familyhelper.life.stack.bean.dto.PbFile;
import com.dwarfeng.familyhelper.life.stack.bean.dto.PbFileUploadInfo;
import com.dwarfeng.familyhelper.life.stack.bean.entity.PbFileInfo;
import com.dwarfeng.familyhelper.webapi.stack.handler.system.TokenHandler;
import com.dwarfeng.familyhelper.webapi.stack.service.life.PbFileResponseService;
import com.dwarfeng.subgrade.sdk.bean.dto.FastJsonResponseData;
import com.dwarfeng.subgrade.sdk.bean.dto.JSFixedFastJsonPagedData;
import com.dwarfeng.subgrade.sdk.bean.dto.PagingUtil;
import com.dwarfeng.subgrade.sdk.bean.dto.ResponseDataUtil;
import com.dwarfeng.subgrade.sdk.bean.key.WebInputLongIdKey;
import com.dwarfeng.subgrade.sdk.interceptor.analyse.BehaviorAnalyse;
import com.dwarfeng.subgrade.sdk.interceptor.http.BindingCheck;
import com.dwarfeng.subgrade.sdk.interceptor.login.LoginRequired;
import com.dwarfeng.subgrade.sdk.interceptor.permission.PermissionRequired;
import com.dwarfeng.subgrade.stack.bean.BeanTransformer;
import com.dwarfeng.subgrade.stack.bean.dto.PagedData;
import com.dwarfeng.subgrade.stack.bean.dto.PagingInfo;
import com.dwarfeng.subgrade.stack.bean.key.LongIdKey;
import com.dwarfeng.subgrade.stack.bean.key.StringIdKey;
import com.dwarfeng.subgrade.stack.exception.ServiceExceptionMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * 个人最佳文件控制器。
 *
 * @author DwArFeng
 * @since 1.0.7
 */
@RestController
@RequestMapping("/api/v1/life")
public class PbFileController {

    private static final Logger LOGGER = LoggerFactory.getLogger(PbFileController.class);

    /**
     * IO 传输设定的缓冲容量。
     */
    private static final int IO_TRANS_BUFFER_SIZE = 4096;

    private final PbFileResponseService service;

    private final ServiceExceptionMapper sem;

    private final BeanTransformer<PbFileInfo, JSFixedFastJsonPbFileInfo> beanTransformer;

    private final CommonsMultipartResolver commonsMultipartResolver;

    private final TokenHandler tokenHandler;

    public PbFileController(
            PbFileResponseService service,
            ServiceExceptionMapper sem,
            BeanTransformer<PbFileInfo, JSFixedFastJsonPbFileInfo> beanTransformer,
            CommonsMultipartResolver commonsMultipartResolver,
            TokenHandler tokenHandler
    ) {
        this.service = service;
        this.sem = sem;
        this.beanTransformer = beanTransformer;
        this.commonsMultipartResolver = commonsMultipartResolver;
        this.tokenHandler = tokenHandler;
    }

    @GetMapping("/pb-file/{longId}/exists")
    @BehaviorAnalyse
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.exists")
    public FastJsonResponseData<Boolean> exists(HttpServletRequest request, @PathVariable("longId") Long longId) {
        try {
            boolean exists = service.exists(new LongIdKey(longId));
            return FastJsonResponseData.of(ResponseDataUtil.good(exists));
        } catch (Exception e) {
            LOGGER.warn("Controller 异常, 信息如下: ", e);
            return FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
    }

    @GetMapping("/pb-file/{longId}")
    @BehaviorAnalyse
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.get")
    public FastJsonResponseData<JSFixedFastJsonPbFileInfo> get(
            HttpServletRequest request, @PathVariable("longId") Long longId
    ) {
        try {
            PbFileInfo pbFileInfo = service.get(new LongIdKey(longId));
            return FastJsonResponseData.of(ResponseDataUtil.good(JSFixedFastJsonPbFileInfo.of(pbFileInfo)));
        } catch (Exception e) {
            LOGGER.warn("Controller 异常, 信息如下: ", e);
            return FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
    }

    @GetMapping("pb-record/{pbRecordId}/pb-file/default")
    @BehaviorAnalyse
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.child_for_pb_record")
    public FastJsonResponseData<JSFixedFastJsonPagedData<JSFixedFastJsonPbFileInfo>> childForPbRecord(
            HttpServletRequest request,
            @PathVariable("pbRecordId") long pbRecordId,
            @RequestParam("page") int page, @RequestParam("rows") int rows
    ) {
        try {
            PagedData<PbFileInfo> childForPbRecord = service.childForPbRecord(
                    new LongIdKey(pbRecordId), new PagingInfo(page, rows)
            );
            PagedData<JSFixedFastJsonPbFileInfo> transform = PagingUtil.transform(
                    childForPbRecord, beanTransformer
            );
            return FastJsonResponseData.of(ResponseDataUtil.good(JSFixedFastJsonPagedData.of(transform)));
        } catch (Exception e) {
            LOGGER.warn("Controller 异常, 信息如下: ", e);
            return FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
    }

    @GetMapping("pb-record/{pbRecordId}/pb-file/inspected-date-desc")
    @BehaviorAnalyse
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.child_for_pb_record_inspected_date_desc")
    public FastJsonResponseData<JSFixedFastJsonPagedData<JSFixedFastJsonPbFileInfo>> childForPbRecordInspectedDateDesc(
            HttpServletRequest request,
            @PathVariable("pbRecordId") long pbRecordId,
            @RequestParam("page") int page, @RequestParam("rows") int rows
    ) {
        try {
            PagedData<PbFileInfo> childForPbRecord = service.childForPbRecordInspectedDateDesc(
                    new LongIdKey(pbRecordId), new PagingInfo(page, rows)
            );
            PagedData<JSFixedFastJsonPbFileInfo> transform = PagingUtil.transform(
                    childForPbRecord, beanTransformer
            );
            return FastJsonResponseData.of(ResponseDataUtil.good(JSFixedFastJsonPagedData.of(transform)));
        } catch (Exception e) {
            LOGGER.warn("Controller 异常, 信息如下: ", e);
            return FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
    }

    @GetMapping("pb-record/{pbRecordId}/pb-file/origin-name-asc")
    @BehaviorAnalyse
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.child_for_pb_record_origin_name_asc")
    public FastJsonResponseData<JSFixedFastJsonPagedData<JSFixedFastJsonPbFileInfo>> childForPbRecordOriginNameAsc(
            HttpServletRequest request,
            @PathVariable("pbRecordId") long pbRecordId,
            @RequestParam("page") int page, @RequestParam("rows") int rows
    ) {
        try {
            PagedData<PbFileInfo> childForPbRecord = service.childForPbRecordOriginNameAsc(
                    new LongIdKey(pbRecordId), new PagingInfo(page, rows)
            );
            PagedData<JSFixedFastJsonPbFileInfo> transform = PagingUtil.transform(
                    childForPbRecord, beanTransformer
            );
            return FastJsonResponseData.of(ResponseDataUtil.good(JSFixedFastJsonPagedData.of(transform)));
        } catch (Exception e) {
            LOGGER.warn("Controller 异常, 信息如下: ", e);
            return FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
    }

    @GetMapping("pb-record/{pbRecordId}/pb-file/uploaded-date-asc")
    @BehaviorAnalyse
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.child_for_pb_record_uploaded_date_asc")
    public FastJsonResponseData<JSFixedFastJsonPagedData<JSFixedFastJsonPbFileInfo>> childForPbRecordUploadedDateAsc(
            HttpServletRequest request,
            @PathVariable("pbRecordId") long pbRecordId,
            @RequestParam("page") int page, @RequestParam("rows") int rows
    ) {
        try {
            PagedData<PbFileInfo> childForPbRecord = service.childForPbRecordUploadedDateAsc(
                    new LongIdKey(pbRecordId), new PagingInfo(page, rows)
            );
            PagedData<JSFixedFastJsonPbFileInfo> transform = PagingUtil.transform(
                    childForPbRecord, beanTransformer
            );
            return FastJsonResponseData.of(ResponseDataUtil.good(JSFixedFastJsonPagedData.of(transform)));
        } catch (Exception e) {
            LOGGER.warn("Controller 异常, 信息如下: ", e);
            return FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
    }

    @GetMapping("/pb-file/{pbFileId}/download")
    @BehaviorAnalyse
    @BindingCheck
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.download_pb_file")
    public ResponseEntity<Object> downloadPbFile(
            HttpServletRequest request, @PathVariable("pbFileId") Long pbFileId
    ) {
        HttpHeaders headers = new HttpHeaders();
        Object body;
        try {
            StringIdKey accountKey = tokenHandler.getUserKey(request);
            PbFile pbFile = service.downloadPbFile(accountKey, new LongIdKey(pbFileId));
            // 将文件名转换成 HTTP 标准文件名编码下的格式。
            String fileName = adjustFileNameEncoding(pbFile.getOriginName());
            headers.add("Content-Disposition", "attachment;filename=" + fileName);
            body = pbFile.getContent();
        } catch (Exception e) {
            body = FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
        return new ResponseEntity<>(body, headers, HttpStatus.OK);
    }

    @SuppressWarnings("DuplicatedCode")
    @PostMapping("/pb-record/{pbRecordId}/pb-file/upload")
    @BehaviorAnalyse
    @BindingCheck
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.upload_pb_file")
    public FastJsonResponseData<Object> uploadPbFile(
            HttpServletRequest request, @PathVariable("pbRecordId") Long pbRecordId
    ) {
        try {
            // 通过请求解析用户。
            StringIdKey accountKey = tokenHandler.getUserKey(request);

            // 确认请求合法。
            if (!commonsMultipartResolver.isMultipart(request)) {
                throw new IllegalStateException("请求不是标准的文件上传请求");
            }

            //获取 multiRequest 中的文件。
            MultipartHttpServletRequest multipartHttpServletRequest = commonsMultipartResolver.resolveMultipart(request);
            MultipartFile file = multipartHttpServletRequest.getFile("file");
            if (Objects.isNull(file)) {
                throw new IllegalStateException("请求体中缺少 file 属性");
            }

            // 解析文件内容。
            String originFileName = file.getOriginalFilename();
            byte[] content;
            try (InputStream in = file.getInputStream(); ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
                IOUtil.trans(in, bout, IO_TRANS_BUFFER_SIZE);
                bout.flush();
                content = bout.toByteArray();
            }

            // 将文件内容转换为接口需要的格式，并上传。
            service.uploadPbFile(
                    accountKey, new PbFileUploadInfo(new LongIdKey(pbRecordId), originFileName, content)
            );

            // 返回响应结果。
            return FastJsonResponseData.of(ResponseDataUtil.good(null));
        } catch (Exception e) {
            LOGGER.warn("Controller 异常, 信息如下: ", e);
            return FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
    }

    @PostMapping("/pb-file/remove")
    @BehaviorAnalyse
    @BindingCheck
    @LoginRequired
    @PermissionRequired("webapi.controller_permitted.life.pb_file.remove_pb_file")
    public FastJsonResponseData<Object> removePbFile(
            HttpServletRequest request, @RequestBody WebInputLongIdKey pbFileKey
    ) {
        try {
            StringIdKey accountKey = tokenHandler.getUserKey(request);
            service.removePbFile(accountKey, WebInputLongIdKey.toStackBean(pbFileKey));
            return FastJsonResponseData.of(ResponseDataUtil.good(null));
        } catch (Exception e) {
            LOGGER.warn("Controller 异常, 信息如下: ", e);
            return FastJsonResponseData.of(ResponseDataUtil.bad(e, sem));
        }
    }

    private String adjustFileNameEncoding(String fileName) {
        return new String(fileName.getBytes(), StandardCharsets.ISO_8859_1);
    }
}
